home *** CD-ROM | disk | FTP | other *** search
- //--------------------------------------------------------------
- //
- // qbe2dmd.prg
- //
- // This utility can convert a Visual dBASE 5.x query to
- // a Visual dBASE 7 data module. You can call it as a
- // function with 2 optional parameters. If you call it with
- // no parameters, a dialog opens for selecting the query
- // to convert. The function returns true if it creates a
- // data module file without error.
- //
- // Syntax:
- //
- // qbe2dmd( [<expC: Query file name>] [,<expL: overwrite] )
- //
- // Example: To create orders.dmd from orders.qbe and
- // overwrite an existing orders.dmd without
- // confirmation.
- //
- // qbe2dmd( "orders.qbe", true )
- //
- // Dependencies:
- //
- // none
- //
- // Visual dBASE Samples Group
- //
- // $Revision: 1.1 $
- //
- // Copyright (c) 1997, Borland International, Inc.
- // All rights reserved.
- //
- //---------------------------------------------------------------//
- //
-
- function qbe2dmd( sQbe, bOverwrite )
- // Call create data module function if data module and
- // QBE file names are valid.
- local bOK, sDmd
- private prQbe
- private prOverwrite
-
- bOK = false
- prQbe = sQbe
- prOverwrite = bOverwrite
-
- if ( PCOUNT() == 0 OR TYPE( "prQbe" ) <> "C" )
- sQbe = UPPER( GETFILE("*.QBE", ;
- "Convert Query to Data Module", ;
- true,false) )
- else
- sQbe = UPPER( sQbe )
- endif
-
- if ( PCOUNT() < 2 OR TYPE( "prOverwrite" ) <> "L" )
- bOverwrite = false
- endif
-
- if ( FILE( sQbe ) AND ( RIGHT( sQbe, 4 ) == ".QBE") )
- sDmd = SUBSTR( sQbe, 1, LEN( sQbe ) - 3 ) + "DMD"
-
- if ( FILE( sDmd ) AND ( NOT bOverwrite ) )
- bOK = ( MSGBOX( sDmd + ;
- " already exists. Overwrite it?", ;
- "Already Exists", ;
- 4 + 32) == 6 )
- else
- bOK = true
- endif
-
- if ( bOK )
- bOK = createDmd( sQbe, sDmd, true )
- endif
-
- endif
-
- return ( bOK )
-
-
-
- function createDmd( sQbe, sDmd, bSession )
- // create a data module from a query
- local fDmd, sPath, sName, sDatabase, sPrimary, aTable, aRel, nTop, oNamer, aProp, aAlias, n1
-
- create session
- set exact off
- set fullpath on
- set space on
-
- // Switch to .QBE directory
- sPath = extractPath( sQbe )
- if not empty( sPath )
- set directory to ( sPath )
- endif
-
- try
- set view to ( sQbe )
- catch ( Exception e )
- msgbox( e.message, "Cannot open QBE", 16 )
- return false
- endtry
-
- // Extract file name from full path and extension, and convert to proper case
- sName = proper( extractFileName( sDmd ) )
-
- // See if database is open
- sDatabase = database()
-
- // Get primary work area of QBE
- sPrimary = alias()
-
- // Find all open tables
- aTable = new Array()
- for n1 = 1 to 225
- select ( n1 )
- if not empty( alias() )
- aTable.add( alias() )
- endif
- endfor
-
- if aTable.size == 0
- msgbox( "No tables opened by QBE", "Nothing to do", 48 )
- return false
- endif
-
- // Sort related tables and get list of relations
- aRel = sortRelatedTables( aTable )
-
- // Initialize name mangler
- oNamer = new NameMangler()
- oNamer.add( "className", "rowset" )
-
- fDmd = new DesignerFile()
- fDmd.create( sDmd )
- fDmd.writeEndHeader()
- fDmd.writeClassLine( sName + "DataModule", "DATAMODULE" )
-
- nTop = 0
-
- if bSession
- // Use Session object if specified
- aProp = { { "top", nTop } }
- fDmd.writeComponent( "SESSION1", "Session", aProp, true )
- oNamer.add( "SESSION1" )
- endif
-
- // Create macro-commands
- #define APROP_NEW nTop += 2; aProp = { { "top", nTop } }
- #define APROP_SESSION if bSession; aProp.add( { "session", "parent.SESSION1" } ) ; endif
- #define APROP_ACTIVE aProp.add( { "active", "true" } )
-
- // Stream database if necessary
- if not empty( sDatabase )
- APROP_NEW
- APROP_SESSION
- aProp.add( { "databaseName", ["] + sDatabase + ["] } )
- APROP_ACTIVE
- sDatabase := oNamer.safeName( sDatabase, true )
- fDmd.writeComponent( sDatabase, "Database", aProp, true )
- endif
-
- aAlias = new AssocArray()
- // Stream each table as a query
- for n1 = 1 to aTable.size
- select ( aTable[ n1 ] )
- APROP_NEW
- APROP_SESSION
- if not empty( sDatabase )
- aProp.add( { "database", "parent." + sDatabase } )
- endif
- if empty( sDatabase )
- aProp.add( { "sql", ['select * from "] + dbf() + ["'] } )
- else
- aProp.add( { "sql", ['select * from ] + extractFileName( dbf() ) + ['] } )
- endif
- APROP_ACTIVE
- aAlias[ alias() ] = oNamer.safeName( alias(), true )
- fDmd.writeComponent( aAlias[ alias() ], "Query", aProp, true )
-
- // Rowset properties
- aProp = new Array()
- if not empty( order() )
- aProp.add( { "indexName", ["] + order() + ["] } )
- endif
- if aRel.isKey( alias() )
- aProp.add( { "masterRowset", "parent.parent." + aAlias[ aRel[ alias() ][ 1 ] ] + ".rowset" } )
- aProp.add( { "masterFields", ["] + aRel[ alias() ][ 2 ] + ["] } )
- endif
- if aProp.size > 0
- fDmd.writeWith( "this." + aAlias[ alias() ] + ".rowset", aProp )
- endif
- endfor
-
- // Assign primary rowset
- if not empty( sPrimary )
- fDmd.writeAssign( "this.rowset", "this." + aAlias[ sPrimary ] + ".rowset" )
- endif
-
- fDmd.writeEndClass()
- fDmd.close()
-
- return FILE( sDmd )
-
-
-
- function extractFileName( sPath )
- return sPath.substring( max( rat( "\", sPath ), rat( ":", sPath ) ), sPath.lastIndexOf( "." ) )
-
-
- function extractPath( sPath )
- return sPath.left( max( rat( "\", sPath ), rat( ":", sPath ) ) )
-
-
-
- function sortRelatedTables( aTable )
- // Make sure related tables are in the proper order (child tables open last)
- // and return relation information for each child table
- local aRel, nTable, nTarget, cTarget, nElem
- aRel = new AssocArray()
- nTable = 1
-
- do while nTable <= aTable.size
- select ( aTable[ nTable ] ) // Go to each work area with an open table
- nTarget = 1
-
- do while not empty( target( nTarget ))
- cTarget = target( nTarget ) // If table is related into another work area
-
- if aRel.isKey( cTarget ) and aRel[ cTarget ][ 1 ] # alias()
- ? "WARNING: ", cTarget, "has more than one master. Using", alias()
- endif
- // Store relation information in AssocArray
- aRel[ cTarget ] = { alias(), relation( nTarget ) }
-
- nElem = aTable.scan( cTarget )
- if nElem < nTable // If target is in an "earlier" work area
- aTable.delete( nElem ) // move it to the end
- aTable[ aTable.size ] := cTarget
- nTable--
- endif
-
- nTarget++
- enddo
-
- nTable++
- enddo
-
- return aRel
-
-
-
- class DesignerFile of File
-
- function writeEndHeader()
- this.writeln( "** END HEADER -- do not remove this line" )
- this.writeln( "//" )
- this.writeln( "// Generated on " + date() )
- this.writeln( "//" )
-
- function writeClassLine( sName, sSuper, bCustom )
- this.writeln( "class " + sName + " of " + sSuper + iif( bCustom, " custom", "" ) )
- this.writeln( "" )
-
- function writeComponent( sName, sClass, aProps, bParent )
- local nProp
- this.writeln( " this." + sName + " = new " + sClass + "()" )
- if bParent
- this.writeln( " this." + sName + ".parent = this" )
- endif
- if aProps.size > 0
- this.writeWith( "this." + sName, aProps )
- else
- this.writeln( "" )
- endif
-
- function writeWith( sName, aProps )
- this.writeln( " with (" + sName + ")" )
- for nProp = 1 to aProps.size
- this.writeAssign( aProps[ nProp ][ 1 ], aProps[ nProp ][ 2 ], 2 )
- endfor
- this.writeln( " endwith" )
- this.writeln( "" )
-
- function writeAssign( sProp, sValue, nLevel )
- if argcount() < 3
- nLevel := 1
- endif
- this.writeln( space( 3 * nLevel ) + sProp + " = " + sValue )
-
- function writeEndClass
- this.writeln( "" )
- this.writeln( "endclass" )
-
- endclass
-
-
-
- class NameMangler
- protect wordList
-
- // Use AssocArray to store words to avoid SET EXACT problems with Array::scan()
- this.wordList = new AssocArray()
-
- function add()
- local nArg
- for nArg = 1 to argcount()
- this.wordList[ upper( argvector( nArg ) ) ] = null
- endfor
-
- function safeName( sArg, bEnum )
- local sRet, sName, nGen
- sName = upper( this.safeChars( sArg ) )
- nGen = 1
- if bEnum
- sRet = sName + nGen++ // Use either enumerated name
- else
- sRet = sName // or plain name
- endif // first
- do while this.wordList.isKey( sRet ) // If there's a match
- sRet := sName + nGen++ // tack on the generated number
- enddo // until there is no match
- this.add( sRet )
- return sRet
-
- function safeChars( sArg )
- local sRet, c1, nChar
- sRet = ""
- for nChar = 1 to len( sArg )
- c1 = substr( sArg, nChar, 1 )
- if isalpha( c1 ) or c1 == "_" or c1 $ "0123456789"
- sRet += c1
- endif
- endfor
- if not isalpha( left( sRet, 1 ) ) // If first character is not valid
- sRet = "X" + sRet // Add default valid character
- endif
- return sRet
-
- endclass
-